package com.abocode.aichat.server.context.channel;

import com.abocode.aichat.data.Entity;
import com.abocode.aichat.exception.ServerContextException;
import com.abocode.aichat.server.context.pipeline.UserOnlinePipeline;
import io.netty.channel.Channel;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author guanxf
 */
public class ProtocolChannelRegistryImpl implements ProtocolChannelRegistry {

    private static ReentrantLock lock = new ReentrantLock();

    private Map<Channel, ChannelWrapper> channelWrappers = new HashMap<>();

    private static Map<Entity, List<Channel>> channelByEntityWrappers = new HashMap<>();

    public boolean containsKey(Channel channel) {
        return channelWrappers.containsKey(channel);
    }

    public boolean containsKey(Entity entity) {
        return channelByEntityWrappers.containsKey(entity);
    }

    @Override
    public void register(Channel channel) {
        try {
            lock.lock();
            if (containsKey(channel)) {
                return;
            }
            channelWrappers.put(channel, new ChannelWrapper(channel));
        } finally {
            lock.unlock();
        }
    }


    @Override
    public void registerEntity(Channel channel, Entity entity) {
        try {
            lock.lock();
            ChannelWrapper channelWrapper = get(channel);
            channelWrapper.registerEntity(entity);
            List<Channel> channels = channelByEntityWrappers.get(entity);
            if (channels == null) {
                channels = new ArrayList<>();
            }
            channels.add(channel);
            channelByEntityWrappers.put(entity, channels);
            userOnlinePipeline.presence(entity);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void unregister(Channel channel) {
        try {
            lock.lock();
            if (containsKey(channel)) {
                ChannelWrapper channelWrapper = get(channel);
                channelWrappers.remove(channel);
                if (channelWrapper.getEntity() != null) {
                    List<Channel> channels = channelByEntityWrappers.get(channelWrapper.getEntity());
                    if (channels != null) {
                        channels.remove(channel);
                        if (channels.isEmpty()) {
                            userOnlinePipeline.offline(channelWrapper.getEntity());
                        }
                    }
                }
            } else {
                throw new RuntimeException("channel not register");
            }
        } finally {
            lock.unlock();
        }
    }

    @Override
    public List<Channel> get(Entity entity) {
        try {
            lock.lock();
            if (containsKey(entity)) {
                return channelByEntityWrappers.get(entity);
            } else {
                return null;
            }
        } finally {
            lock.unlock();
        }
    }


    @Override
    public ChannelWrapper get(Channel channel) {

        try {
            lock.lock();
            if (containsKey(channel)) {
                return channelWrappers.get(channel);
            } else {
                throw new ServerContextException();
            }
        } finally {
            lock.unlock();
        }
    }

    private UserOnlinePipeline userOnlinePipeline;
    public void setUserOnlinePipeline(UserOnlinePipeline userOnlinePipeline) {
        this.userOnlinePipeline = userOnlinePipeline;
    }

    @Override
    public boolean isOnline(Entity entity) {
        return userOnlinePipeline.isOnline(entity);
    }

    @Override
    public String domain(Entity entity) {
        return userOnlinePipeline.domain(entity);
    }

    @Override
    public void presence(Entity entity) {
        userOnlinePipeline.presence(entity);
    }
}
